/*
 token.go
 Created by hupfei on 2019-10-17
*/

package service

import (
	"database/sql"
	"encoding/json"
	"github.com/alecthomas/template"
	"github.com/pkg/errors"
	"math"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"
	"zimuzu_cvn_web_api/config"
	"zimuzu_cvn_web_api/model"
	"zimuzu_cvn_web_api/pkg/drive/redis"
	. "zimuzu_cvn_web_api/pkg/e"
	"zimuzu_cvn_web_api/pkg/errno"
)

const TokenMaxPledgeNum = 100          //最大批量质押数
const TokenRmbPrice = 600              //令牌等值的人民币价格
const TokenOrderPayValidTime = 120     //订单有效支付时长秒
const TokenUsdtOrderPayValidTime = 300 //USDT令牌订单支付有效时长秒
const TokenUsdtOrderMaxNum = 3         //USDT令牌订单最大允许的同时挂售数
const TokenSaleOrderOverdueMaxNum = 3  //USDT令牌订单最大允许的支付超时次数
const TokenOldBatch = 99               //旧令牌批次标识
const TokenBuyBackMaxNum = 200         //每日最大回购令牌数量

const TokenOrderStatusSaling = 1
const TokenOrderStatuslucking = 2
const TokenOrderStatusOverdue = 3
const TokenOrderStatusComplete = 4
const TokenOrderStatusCancel = 5

//获取生成令牌任务缓存键
//Auth:Xu
func GetGenTokenTaskCacheKey(key string) string {
	return "cvn:public:genTokenTask:" + key
}

//获取生成令牌任务缓存键
//Auth:Xu
func GetTokenSaleOverdueCountCacheKey(uid int64) string {
	uidStr := strconv.FormatInt(uid, 10)
	return "user:" + uidStr + ":buyTokenOrder:overDue"
}

type KLineData struct {
	ID     int64   `json:"id"`     // K线ID
	Amount float64 `json:"amount"` // 成交量
	Count  int64   `json:"count"`  // 成交笔数
	Open   float64 `json:"open"`   // 开盘价
	Close  float64 `json:"close"`  // 收盘价, 当K线为最晚的一根时, 时最新成交价
	Low    float64 `json:"low"`    // 最低价
	High   float64 `json:"high"`   // 最高价
	Vol    float64 `json:"vol"`    // 成交额, 即SUM(每一笔成交价 * 该笔的成交数量)
}

type KLineReturn struct {
	Status  string      `json:"status"`   // 请求处理结果, "ok"、"error"
	Ts      int64       `json:"ts"`       // 响应生成时间点, 单位毫秒
	Data    []KLineData `json:"data"`     // KLine数据
	Ch      string      `json:"ch"`       // 数据所属的Channel, 格式: market.$symbol.kline.$period
	ErrCode string      `json:"err-code"` // 错误代码
	ErrMsg  string      `json:"err-msg"`  // 错误提示
}

//查询cvnt涨幅价格
//Auth:Xu
func GetCvntGain(period string, size int) (kLineReturn KLineReturn, err error) {
	urlStr := config.C.JapenUrl + "/v1/market/getMarketKline?strSymbol=cvntbtc&strPeriod=" + period + "&nSize=" + strconv.Itoa(size)
	errInfo := "查询cvnt价格失败"
	if err = HttpGet(urlStr, &kLineReturn); err != nil {
		err = errors.New(errInfo)
		Log("err", errInfo, err.Error())
		return
	}
	if kLineReturn.Status != "ok" {
		err = errors.New("回购人数太多，请稍后再试")
	}

	return
}

//查询周期内的最大涨幅
//Auth:Xu
func GetCvntGainMax(period string, size int) (maxGain float64, err error) {
	cvntGain, err := GetCvntGain(period, size)
	if err != nil {
		return
	}
	cvntPriceHighSlice := []float64{}
	timeSlice := []int64{}
	cvntMap := make(map[int64]float64)
	for _, gain := range cvntGain.Data {
		cvntMap[gain.ID] = gain.Close
		cvntPriceHighSlice = append(cvntPriceHighSlice, gain.High)
		timeSlice = append(timeSlice, gain.ID)
	}
	sort.Slice(timeSlice, func(i, j int) bool {
		return timeSlice[i] > timeSlice[j]
	})
	sort.Slice(cvntPriceHighSlice, func(i, j int) bool {
		return cvntPriceHighSlice[i] > cvntPriceHighSlice[j]
	})

	currentPrice := cvntMap[timeSlice[0]]
	maxPrice := cvntPriceHighSlice[0]

	maxGain = currentPrice / maxPrice

	return
}

//查询今天全网剩余可回购的令牌数、及排队令牌数量
//Auth:Xu
func GetTodayRestBuyBackNum() (resNum, rankNum int, err error) {
	dealNum, err := GetTodayDealBuyBackNum()
	if err != nil {
		return
	}
	rankNum, err = model.GetBuyBackQueueNum(config.C.DB.DB)
	if err != nil {
		err = errors.New("查询全网排队令牌数量失败")
		return
	}
	if rankNum > 0 {
		resNum = 0
		return
	} else {
		resNum = TokenBuyBackMaxNum - dealNum
	}
	return
}

//查询今天全网已回购的令牌数、及排队令牌数量2
//Auth:Xu
func GetTodayRestBuyBackNum2() (dealNum, rankNum int, err error) {
	dealNum, err = model.GetTodayBuyBackNum2(config.C.DB.DB)
	if err != nil {
		return
	}
	rankNum, err = model.GetBuyBackQueueNum2(config.C.DB.DB)
	if err != nil {
		err = errors.New("查询全网排队令牌数量失败")
		return
	}

	return
}

//查询今日全网已回购的令牌数量
//Auth:Xu
func GetTodayDealBuyBackNum() (num int, err error) {
	num, err = model.GetTodayBuyBackNum(config.C.DB.DB)
	if err != nil {
		err = errors.New("查询全网回购令牌数书失败")
		return
	}
	return
}

//查询用户排队信息
//Auth:Xu
func GetUserBuybackRankInfo(uid int64) (userRank, haveDealNum, rankNum int, err error) {
	var restNum int
	restNum, rankNum, err = GetTodayRestBuyBackNum()
	haveDealNum = TokenBuyBackMaxNum - restNum
	if rankNum != 0 {
		userRank, err = model.GetUserBuybackQueueRank(config.C.DB.DB, uid)
	}
	return

}

//查询用户今日剩余可回购令牌数量
//Auth:Xu
func GetUserTodayRestBuyBackNum(uid int64) (num int, err error) {
	//查询自有令牌数
	selfNum, err := model.GetTokenNum(uid, 0, 0, 1)
	if err != nil {
		return
	}
	maxNum := GetUserDayMaxBuybackNum(selfNum)

	todatBuyBackComplete, err := model.GetUserTodayBuybackTokenNum(uid)
	if err != nil {
		err = errors.New("查询今日已回购令牌数失败")
		return
	}
	if uid == 8042615 || uid == 7720644 || uid == 6573444 || uid == 7294588 || uid == 8114729 || uid == 8113636 || uid == 5724782 || uid == 5512533 {
		maxNum = selfNum
	}
	num = maxNum - todatBuyBackComplete
	if num < 0 {
		num = 0
	}
	return
}

//查询用户每天最大回购数量
//Auth:Xu
func GetUserDayMaxBuybackNum(selfNum int) (maxNum int) {
	switch {
	case selfNum < 10:
		maxNum = 2
	case selfNum >= 10 && selfNum < 100:
		maxNum = 5
	case selfNum >= 100 && selfNum < 500:
		maxNum = 8
	case selfNum >= 500:
		maxNum = 10
	}

	return
}

/*
 * @ Author: hupfei
 * @ Description: 超级节点列表
 **/
type SuperNodeRes struct {
	Uid              int64  `json:"uid,omitempty"`
	Name             string `json:"name"`
	Pic              string `json:"pic"`
	WechatCodePic    string `json:"wechatCodePic"`
	Wechat           string `json:"wechat"`
	Des              string `json:"des"`
	Introduction     string `json:"introduction"`
	RemainingNodeNum int    `json:"remainingNodeNum"`
}

func GetSuperNodeList() (info []SuperNodeRes, err error) {
	key := PublicCacheKeyPre + "superNodeList"
	if redis.Exists(key) {
		infoStr, _ := redis.Get(key)
		err := json.Unmarshal([]byte(infoStr), &info)
		return info, err
	}

	res, err := model.GetSuperNodeInfoList()
	if err != nil {
		return nil, err
	}
	//旗下挖矿节点数
	pledgedNodeNumList, err := model.GetAllSuperNodePledgedNodeNum()
	if err != nil {
		return nil, errno.DbSearchError
	}

	for _, v := range res {
		var super SuperNodeRes
		super.Uid = v.Uid
		super.Name = v.Name
		super.Pic = v.Pic
		super.WechatCodePic = v.WechatCodePic
		super.Wechat = v.Wechat
		super.Des = v.Des
		super.Introduction = v.Introduction

		index := -1
		for k, v1 := range pledgedNodeNumList {
			if v1.NodeId == v.NodeId {
				index = k
				break
			}
		}
		if index == -1 {
			//没找到对应的超级节点
			super.RemainingNodeNum = v.MaxNodeNum
		} else {
			super.RemainingNodeNum = v.MaxNodeNum - pledgedNodeNumList[index].Num
		}
		info = append(info, super)
	}
	infoStr, _ := json.Marshal(info)
	_, _ = redis.Set(key, infoStr, 5*TimeFormatMin)
	return info, nil
}

type TokenUser struct {
	Phone               string  `json:"phone"`
	Area                int     `json:"area"`
	SaleOrderOverdueNum int     `json:"saleOrderOverdueNum"`
	SuperNodeName       string  `json:"superNodeName"`
	TodayCanPledgeNum   int     `json:"todayCanPledgeNum"`
	DepositCvnt         float64 `json:"depositCvnt"`
	LastIncomeAbility   float64 `json:"lastIncomeAbility" db:"last_income_ability"`
	RemainingNodeNum    int     `json:"remainingNodeNum"`
	Num                 struct {
		IdleNum   int `json:"idleNum"`
		MiningNum int `json:"miningNum"`
		RentNum   int `json:"rentNum"`
		SaleNum   int `json:"saleNum"`
	} `json:"num"`
	RentNum struct {
		IdleNum       int `json:"idleNum"`
		MiningNum     int `json:"miningNum"`
		RemainingTime int `json:"remainingTime"`
		ReturnNum     int `json:"returnNum"`
	} `json:"rentNum"`
	Linux struct {
		NodeNum       int64 `json:"nodeNum"`
		NodePledgeNum int   `json:"nodePledgeNum"`
		//TokenNum      int   `json:"tokenNum"`
		//TokenRentNum  int   `json:"tokenRentNum"`
	} `json:"linux"`
	Pc struct {
		NodeNum       int64 `json:"nodeNum"`       //节点数
		NodePledgeNum int   `json:"nodePledgeNum"` //质押了令牌的节点数
		//TokenNum      int   `json:"tokenNum"`      //自有的令牌数量
		//TokenRentNum  int   `json:"tokenRentNum"`  //租用的令牌数量
	} `json:"pc"`
	Router struct {
		NodeNum       int64 `json:"nodeNum"`
		NodePledgeNum int   `json:"nodePledgeNum"`
		//TokenNum               int   `json:"tokenNum"`
		//TokenRentNum           int   `json:"tokenRentNum"`
		TokenPledgedNum        int `json:"tokenPledgedNum"`        //质押的令牌数量
		IncompleteTokenNum     int `json:"incompleteTokenNum"`     //自有可用碎片数量
		IncompleteTokenRentNum int `json:"incompleteTokenRentNum"` //租借可用碎片数量
	} `json:"router"`
	Notification struct {
		BorrowStatus int `json:"borrowStatus"` //借入状态 0无新的借入，1有新的借入
		LeaseStatus  int `json:"leaseStatus"`  //租借状态 0保证金充足 1保证金不足
	} `json:"notification"`
}

/*
 * @ Author: hupfei
 * @ Description: 用户信息
 **/
func TokenUserInfo(uid int64) (info TokenUser, err error) {

	user, err := GetUserInfo(uid)
	if err != nil {
		return
	}
	cacheKey := GetTokenSaleOverdueCountCacheKey(uid)
	saleOrderOverdueNumStr, _ := redis.Get(cacheKey)
	overdueNum, _ := strconv.Atoi(saleOrderOverdueNumStr)
	info.SaleOrderOverdueNum = overdueNum
	info.Phone = user.PhoneNumber
	info.Area = user.PhoneArea

	//超级节点名字
	info.SuperNodeName, err = model.GetSuperNodeNameByMinerUid(uid)
	if err != nil {
		return
	}

	//自有令牌
	info.Num.MiningNum, _ = model.GetTokenNum(uid, 0, model.TokenUsedStatusMining, 1)
	info.Num.IdleNum, _ = model.GetTokenNum(uid, 0, model.TokenUsedStatusUnused, 1)
	info.Num.RentNum, _ = model.GetTokenNum(uid, 0, 0, 2)
	info.Num.SaleNum, _ = model.GetTokenNum(uid, 0, model.TokenUsedStatusLocking, 1)

	//租借令牌
	info.RentNum.MiningNum, _ = model.GetTokenNum(0, uid, model.TokenUsedStatusMining, 2)
	info.RentNum.IdleNum, _ = model.GetTokenNum(0, uid, model.TokenUsedStatusUnused, 2)

	returnInfo, _ := model.GetRecentlyReturnToken(uid)
	info.RentNum.ReturnNum = returnInfo.DealNum
	info.RentNum.RemainingTime = returnInfo.RemainingTime

	//今日可新增质押数量
	todayPledgedNum, err := model.GetTodayPledgeNum()
	info.TodayCanPledgeNum = config.C.PledgeMaxNum - todayPledgedNum
	if info.TodayCanPledgeNum < 0 {
		info.TodayCanPledgeNum = 0
	}

	//节点数量
	info.Router.NodeNum, _ = model.GetUserNodeNums(uid, 1, model.NodeTypeRouter)
	info.Pc.NodeNum, _ = model.GetUserNodeNums(uid, 1, model.NodeTypePc)
	info.Linux.NodeNum, _ = model.GetUserNodeNums(uid, 1, model.NodeTypeLinux)

	//绑定了令牌的节点数
	nodeNum, _ := model.GetUserPledgedNodeNum(uid)
	info.Router.NodePledgeNum = 0
	info.Pc.NodePledgeNum = 0
	info.Linux.NodePledgeNum = nodeNum.LinuxNum

	//路由器碎片数量
	info.Router.TokenPledgedNum, _ = model.GetRouterTokenPledgedNum(uid)
	info.Router.IncompleteTokenNum, _ = model.GetRouterNodeNumExceptWholeToken(model.TokenRentStatusNormal, uid)
	info.Router.IncompleteTokenRentNum, _ = model.GetRouterNodeNumExceptWholeToken(model.TokenRentStatusRented, uid)

	//是否有新的借入
	borrowNum, _ := model.GetBorrowNum(uid)
	key := UserRedisPrefix + strconv.FormatInt(uid, 10) + RedisUserBorrowNum
	if redis.Exists(key) {
		cacheNum, _ := redis.Get(key)
		cacheNumInt, _ := strconv.Atoi(cacheNum)
		if borrowNum != cacheNumInt {
			info.Notification.BorrowStatus = 1
			_, _ = redis.Set(key, strconv.Itoa(borrowNum), -1)
		}
	} else {
		_, _ = redis.Set(key, strconv.Itoa(borrowNum), -1)
	}

	// 保证金可发放天数
	checkData, _ := CheckRentTokenPledgeStatus(uid, 0)
	info.Notification.LeaseStatus = checkData.EnoughDays

	// 保证金
	userAmount, _ := model.GetCvnFrozenInfo(uid, model.PledgeDeposit)
	info.DepositCvnt = userAmount.Amount

	//昨日平均收益能力     update xu 11-13
	info.LastIncomeAbility = GetNetAverageIncome(model.GlobalNetLastAverageIncome)

	//剩余挖矿名额
	miner, err := model.GetMinerInfoByUid(uid)
	if err != nil {
		return
	}
	pledgedNum, _ := model.GetUserPledgedTokenNodeNumExceptRouter(uid)
	info.RemainingNodeNum = miner.MaxNodeNum - pledgedNum

	return info, nil
}

//用于刷新节点信息
func GetNodeInfo(uid int64, Type int) (info TokenUser) {
	info.Num.IdleNum, _ = model.GetTokenNum(uid, 0, model.TokenUsedStatusUnused, 1)
	info.RentNum.IdleNum, _ = model.GetTokenNum(0, uid, model.TokenUsedStatusUnused, 2)
	info.RentNum.MiningNum, _ = model.GetTokenNum(0, uid, model.TokenUsedStatusMining, 2)

	if Type == model.NodeTypePc {
		//节点数量
		info.Pc.NodeNum, _ = model.GetUserNodeNums(uid, 1, model.NodeTypePc)
		//绑定了令牌的节点数
		info.Pc.NodePledgeNum, _ = model.GetUserNodeNumWithType(uid, model.NodeTypePc, 1)
	} else if Type == model.NodeTypeLinux {
		info.Linux.NodeNum, _ = model.GetUserNodeNums(uid, 1, model.NodeTypeLinux)
		info.Linux.NodePledgeNum, _ = model.GetUserNodeNumWithType(uid, model.NodeTypeLinux, 1)
	} else if Type == model.NodeTypeRouter {
		info.Router.NodeNum, _ = model.GetUserNodeNums(uid, 1, model.NodeTypeRouter)
		info.Router.NodePledgeNum, _ = model.GetUserNodeNumWithType(uid, model.NodeTypeRouter, 1)
		//路由器碎片数量
		info.Router.TokenPledgedNum, _ = model.GetRouterTokenPledgedNum(uid)
		info.Router.IncompleteTokenNum, _ = model.GetRouterNodeNumExceptWholeToken(model.TokenRentStatusNormal, uid)
		info.Router.IncompleteTokenRentNum, _ = model.GetRouterNodeNumExceptWholeToken(model.TokenRentStatusRented, uid)
	}

	return
}

//质押令牌/撤销质押
func TokenPledge(uid int64, source, tokenType, nodeType, nodeId int) (err error) {
	if source == 0 { //质押
		superNodeId, err := StartPledgeToken(uid)
		if err != nil {
			return err
		}

		superNodeIdCacheKey := UserRedisPrefix + "pledge:superNodeId:" + strconv.FormatInt(superNodeId, 10)
		ok, err := redis.RedisSetnx(superNodeIdCacheKey, "1", 60)
		if err != nil || !ok {
			return errno.SystemBusyError
		}
		defer redis.UnDelete(superNodeIdCacheKey)

		todayCanPledgeNum := math.MaxInt32
		remainingNodeNum := math.MaxInt32

		//用户闲置令牌数量（自有和租用的）
		totalIdleNum := 0
		remainingCanPledgeNodeNum := 0
		remainingRentCanPledgeNodeNum := 0

		idleNum, err := model.GetTokenNum(uid, 0, model.TokenUsedStatusUnused, 1)
		if err != nil {
			return errno.DbSearchError
		}
		Log("info", "自有的完整令牌数量：", idleNum)
		rentIdleNum, err := model.GetTokenNum(0, uid, model.TokenUsedStatusUnused, 2)
		if err != nil {
			return errno.DbSearchError
		}
		Log("info", "租用的完整令牌数量：", rentIdleNum)

		if nodeType == model.NodeTypeRouter {
			idleNum *= model.OneTokenCanBePledgedMaxNum
			rentIdleNum *= model.OneTokenCanBePledgedMaxNum

			//自有的碎片可质押的数量
			remainingCanPledgeNodeNum, err = model.GetRouterNodeNumExceptWholeToken(model.TokenRentStatusNormal, uid)
			if err != nil {
				return errno.DbSearchError
			}
			Log("info", "自有的碎片可质押的数量：", remainingCanPledgeNodeNum)
			idleNum += remainingCanPledgeNodeNum

			//租用的碎片可质押的数量
			remainingRentCanPledgeNodeNum, err = model.GetRouterNodeNumExceptWholeToken(model.TokenRentStatusRented, uid)
			if err != nil {
				return errno.DbSearchError
			}
			Log("info", "租用的碎片可质押的数量：", remainingRentCanPledgeNodeNum)
			rentIdleNum += remainingRentCanPledgeNodeNum
		}
		totalIdleNum = idleNum + rentIdleNum

		if totalIdleNum == 0 {
			return errors.New("您还没有闲置令牌，无法质押")
		}
		if nodeId > 0 && tokenType == 0 && rentIdleNum == 0 {
			return errors.New("您还没有租用的闲置令牌，无法质押")
		}
		if nodeId > 0 && tokenType == 1 && idleNum == 0 {
			return errors.New("您还没有自有的闲置令牌，无法质押")
		}

		var token model.TokenInfo
		if nodeId > 0 {
			//单个质押
			if nodeType == model.NodeTypeRouter && ((tokenType == 0 && remainingRentCanPledgeNodeNum > 0) || (tokenType == 1 && remainingCanPledgeNodeNum > 0)) {
				//优先质押租用/自有的碎片令牌
				token, err = model.GetIncompleteTokenForMaxNodeNum(uid, tokenType)
			} else {
				token, err = model.GetOneToken(uid, tokenType)
			}
			if err != nil {
				return err
			}
			err = singlePledge(uid, nodeId, token.Id)
			if err != nil {
				return err
			}
		} else {
			//批量质押
			err = batchPledge(uid, todayCanPledgeNum, idleNum, rentIdleNum, remainingNodeNum, nodeType)
			if err != nil {
				return err
			}
		}
	} else { //撤销质押
		if nodeId > 0 {
			//单个解除
			err = CancelPledge(uid, nodeId)
			if err != nil {
				return err
			}
		} else {
			//批量
			cacheKey := UserRedisPrefix + "batchPledge:" + strconv.FormatInt(uid, 10)
			ok, err := redis.RedisSetnx(cacheKey, "1", 60)
			if err != nil || !ok {
				return errno.SystemBusyError
			}
			defer redis.UnDelete(cacheKey)

			ok = model.BatchCancelPledge(uid, nodeType)
			if !ok {
				return errors.New("批量撤销失败")
			}
		}
	}

	return nil
}

//质押一个节点
func singlePledge(uid int64, nodeId, tokenId int) (err error) {
	cacheKey := UserRedisPrefix + "pledge:uid:" + strconv.FormatInt(uid, 10) + ":tokenId:" + strconv.Itoa(tokenId) + ":nodeId:" + strconv.Itoa(nodeId)
	ok, err := redis.RedisSetnx(cacheKey, "1", 60)
	if err != nil || !ok {
		return errno.SystemBusyError
	}
	defer redis.UnDelete(cacheKey)

	err = model.SingelPledge(uid, nodeId, tokenId)

	return
}

//批量质押
func batchPledge(uid int64, todayCanPledgeNum, idleTokenNum, rentIdleTokenNum, remainingNodeNum, nodeType int) (err error) {
	var successNum, failNum int
	cacheKey := UserRedisPrefix + "batchPledge:" + strconv.FormatInt(uid, 10)
	ok, err := redis.RedisSetnx(cacheKey, "1", 60)
	if err != nil || !ok {
		return errno.SystemBusyError
	}
	defer redis.UnDelete(cacheKey)

	//未质押的节点总数
	nodeUnPledgedNum, err := model.GetUserNodeNumWithType(uid, nodeType, 0)
	if err != nil {
		return errno.DbSearchError
	}
	if nodeUnPledgedNum == 0 {
		info := "您还没有未质押的客户端节点"
		if nodeType == model.NodeTypeLinux {
			info = "您还没有未质押的Linux节点"
		} else if nodeType == model.NodeTypeRouter {
			info = "您还没有未质押的路由器节点"
		}
		return errors.New(info)
	}

	//未质押的节点数列表（最多TokenMaxPledgeNum个）
	nodeUnPledgedList, err := model.GetAllNode(uid, nodeType, 0, TokenMaxPledgeNum)
	if err != nil {
		return errno.DbSearchError
	}

	// 一次只能处理 TokenMaxPledgeNum 个
	if nodeUnPledgedNum > len(nodeUnPledgedList) {
		failNum = nodeUnPledgedNum - len(nodeUnPledgedList)
	}

	//质押数量不能超过剩余名额
	if len(nodeUnPledgedList) > remainingNodeNum {
		failNum += len(nodeUnPledgedList) - remainingNodeNum
		nodeUnPledgedList = nodeUnPledgedList[0:remainingNodeNum]
	}

	//节点数超过闲置令牌数量
	if len(nodeUnPledgedList) > idleTokenNum+rentIdleTokenNum {
		failNum += len(nodeUnPledgedList) - idleTokenNum - rentIdleTokenNum
		nodeUnPledgedList = nodeUnPledgedList[0 : idleTokenNum+rentIdleTokenNum]
	}

	//linux pc 已达今日质押上限
	if nodeType != model.NodeTypeRouter && len(nodeUnPledgedList) > todayCanPledgeNum {
		failNum += len(nodeUnPledgedList) - todayCanPledgeNum
		nodeUnPledgedList = nodeUnPledgedList[0:todayCanPledgeNum]
	}

	//令牌列表
	tokenList, err := model.GetUserIdleTokenList(uid, nodeType, len(nodeUnPledgedList))
	if err != nil {
		return errno.DbSearchError
	}

	var wg = new(sync.WaitGroup)
	var lock sync.Mutex
	tokenIndex := 0
	for k, v := range nodeUnPledgedList {
		//增加计数
		wg.Add(1)

		if nodeType == model.NodeTypeRouter {
			//路由器一个令牌可质押五台设备
			v.TokenId = tokenList[tokenIndex].Id

			tokenList[tokenIndex].NodeNum++
			if tokenList[tokenIndex].NodeNum == model.OneTokenCanBePledgedMaxNum {
				tokenIndex++
			}
		} else {
			v.TokenId = tokenList[k].Id
		}

		//开启协程处理一个质押
		Log("info", "开始质押第 ", k, " 个节点，nodeId = ", v.NodeId)
		go func(n model.NodeInfo) {
			defer wg.Done()

			err = singlePledge(uid, n.NodeId, n.TokenId)
			lock.Lock()
			if err == nil {
				successNum++
				Log("info", "质押成功，nodeId = ", n.NodeId)
			} else {
				failNum++
				Log("err", "质押失败，nodeId = ", n.NodeId, "，err = ", err.Error())
			}
			lock.Unlock()
		}(v)
	}

	wg.Wait()
	Log("info", "已全部质押完成")

	if failNum > 0 {
		ErrBatchInfo := "已成功质押 " + strconv.Itoa(successNum) + " 个节点，仍有 " + strconv.Itoa(failNum) + " 个节点没有质押令牌"
		Log("info", ErrBatchInfo)
		return errors.New(ErrBatchInfo)
	}

	return nil
}

//解除一个质押
func CancelPledge(uid int64, nodeId int) (err error) {
	cacheKey := UserRedisPrefix + "cancelPledge:uid:" + ":nodeId:" + strconv.Itoa(nodeId)
	ok, err := redis.RedisSetnx(cacheKey, "1", 60)
	if err != nil || !ok {
		return errno.SystemBusyError
	}
	defer redis.UnDelete(cacheKey)

	tokenId, err := model.GetTokenIdByNodeId(uid, nodeId)
	if err != nil {
		return err
	}
	err = model.CancelSingelPledge(uid, nodeId, tokenId)
	return
}

//实时获取令牌的CVNT价格 ,  新旧令牌区分
//Auth:Xu
func GetCurrentTokenCvntPrice(batch int) (cvntNum int) {
	cvntRate, _, _ := GetCvntPrice()
	if batch == TokenOldBatch {
		if cvntRate < 0.2 {
			cvntRate = 0.2
		}
	}
	cvntNumFloat := TokenRmbPrice / cvntRate
	cvntNum = int(cvntNumFloat)
	return
}

type TokenOrderInfo struct {
	OrderNo    string  `json:"orderNo"`
	TotalPrice int     `json:"totalPrice"`
	Num        int     `json:"num"`
	UsableCvnt float64 `json:"usableCvnt"`
	UnitPrice  int     `json:"unitPrice"`
	ExpireSec  int     `json:"expireSec"`
}

//官方渠道购买令牌下单
//Auth：Xu
func PlatformTradeCreateOrder(uid int64, num int) (err error, returnData TokenOrderInfo) {
	//查询账户余额
	cvnAmount, err := GetUserCvnAmountByUid(uid)
	//查询当前令牌价格
	tokenCvntPrice := GetCurrentTokenCvntPrice(0)
	if cvnAmount.Balance-float64(tokenCvntPrice*num) < 0 {
		return errno.WalletNotEnoughERR, returnData
	}
	//创建订单
	data := model.TokenSaleOrder{
		OrderNo:        GenTokenOrder(model.TokenSaleOrderPlatBuy, uid),
		ParentOrderNo:  "",
		UnitCvntPrice:  tokenCvntPrice,
		TotalCvntPirce: tokenCvntPrice * num,
		UnitUsdtPrice:  0,
		TotalUsdtPrice: 0,
		TokenNum:       num,
		BuyerUid:       uid,
		SellerUid:      0,
		Status:         model.TokenSaleOrderStatusSale,
		PayValidTime:   time.Now().Unix() + TokenOrderPayValidTime,
		OrderType:      model.TokenSaleOrderPlatBuy,
		CreateTime:     time.Now().Unix(),
	}
	err = model.InsertTokenSaleOrder(config.C.DB.DB, data)
	if err != nil {
		err = errors.New("订单生成失败")
		return
	}

	returnData = TokenOrderInfo{
		OrderNo:    data.OrderNo,
		TotalPrice: data.TotalCvntPirce,
		Num:        num,
		UsableCvnt: cvnAmount.Balance,
		UnitPrice:  data.UnitCvntPrice,
		ExpireSec:  TokenOrderPayValidTime,
	}

	return nil, returnData
}

//生成令牌租售订单号  订单类型_日期_uid时间戳后6位
//Auth:Xu
func GenTokenOrder(orderType int, uid int64) string {
	currentTimeStr := time.Now().Format("2006-01-02")
	currentTimeStr = strings.Replace(currentTimeStr, "-", "", -1)
	timeStr := strconv.FormatInt(time.Now().Unix(), 10)[5:10]
	uidStr := strconv.FormatInt(uid, 10)

	return strconv.Itoa(orderType) + currentTimeStr + uidStr + timeStr
}

//官方渠道购买令牌订单支付
//Auth: Xu
func PayTokenSaleOrder(orderNo string, uid int64) (err error) {
	nxCacheKey := "cvn:PayTokenSaleOrder:" + strconv.FormatInt(uid, 10)
	res, err := redis.RedisSetnx(nxCacheKey, "1", 2)
	if err != nil || !res {
		return errors.New("操作过于频繁，请稍后再试")
	}

	orderInfo, err := model.GetTokenOrderInfoByNo(config.C.DB.DB, orderNo)
	if err != nil {
		return errors.New("未查找到订单信息")
	}
	if time.Now().Unix()-orderInfo.PayValidTime > 0 {
		return errors.New("超过订单有效支付时间，请重新下单")
	}
	if orderInfo.PayStatus == model.TokenOrderPayed {
		return errors.New("订单已支付")
	}
	if orderInfo.BuyerUid != uid {
		return errors.New("非法操作")
	}
	conn, _ := config.C.DB.DB.Beginx()

	//生成CVNT流水记录，账户扣款
	updateAmountData := UpdateCvnAmountParams{
		Uid:         uid,
		Amount:      -float64(orderInfo.TotalCvntPirce),
		Source:      model.LogSourceBuyToken,
		RelateId:    strconv.FormatInt(orderInfo.Id, 10),
		RelateTable: "mg_token_sale_order",
	}
	_, err = UpdateCvnAmountBalance(conn, updateAmountData)
	if err != nil {
		conn.Rollback()
		return
	}

	//修改订单状态
	timeNowInt64 := time.Now().Unix()
	res3, err3 := conn.Exec("UPDATE `mg_token_sale_order` SET `pay_status` = 2, pay_time = ? ,complete_time = ?, deal_num = ?, status = ? WHERE `order_no` = ?",
		timeNowInt64, timeNowInt64, orderInfo.TokenNum, TokenOrderStatusComplete, orderNo)
	if err3 != nil {
		conn.Rollback()
		Log("err", err3.Error())
		return errors.New("修改订单支付状态失败")
	} else if resR3, _ := res3.RowsAffected(); resR3 <= 0 {
		conn.Rollback()
		return errors.New("修改订单支付状态失败")
	}

	//异步生成令牌
	err = addGenTokenQueue(orderNo, orderInfo.TokenNum)
	if err != nil {
		conn.Rollback()
		return err
	}

	conn.Commit()

	return
}

//删除金至尊兑换订单缓存
//Auth: Xu
func delTokenOrderCache(orderNo string) {
	redis.SRem(GenTokenSetCacheKey, orderNo)
	taskCacheKey := GetGenTokenTaskCacheKey(orderNo)
	redis.Delete(taskCacheKey)
}

//加入待生成队列
//Auth:Xu
func addGenTokenQueue(orderNo string, tokenNum int) (err error) {
	_, err = redis.SAdd(GenTokenSetCacheKey, orderNo)
	if err != nil {
		return errors.New("生成令牌失败")
	}
	taskCacheKey := GetGenTokenTaskCacheKey(orderNo)
	_, err = redis.Incrby(taskCacheKey, tokenNum)
	if err != nil {
		return errors.New("生成令牌失败2")
	}
	return
}

//执行生成令牌的任务
//Auth:Xu
func GenTokenTask() {
	task, res := redis.SMembers(GenTokenSetCacheKey)
	if res != true {
		return
	}
	for _, val := range task {
		go func(orderNo string) {
			orderInfo, err := model.GetTokenOrderInfoByNo(config.C.DB.DB, orderNo)
			if err != nil {
				return
			}
			taskCacheKey := GetGenTokenTaskCacheKey(orderNo)
			for i := 0; i < orderInfo.TokenNum; i++ {
				dealNum, _ := redis.Decr(taskCacheKey)
				if dealNum >= 0 {
					err = AddToken(orderInfo.BuyerUid, orderNo, model.TokenSaleOrderPlatBuy)
					if err != nil {
						continue
					}
				} else {
					break
				}
			}
			succNum, _ := model.GetSuccTokenNumByOrderNo(config.C.DB.DB, orderNo)
			failNum := orderInfo.TokenNum - succNum
			if failNum == 0 {
				delTokenOrderCache(orderNo)
				model.UpdateTokenSaleOrderPlatformBuy(config.C.DB.DB, orderNo)
				return
			} else {
				redis.Set(taskCacheKey, failNum, -1)
			}

		}(val)
	}
}

//生成令牌及流水
//Auth: Xu
func AddToken(uid int64, orderNo string, orderType int) (err error) {
	conn, _ := config.C.DB.DB.Begin()
	tokenId, err := model.InsertMgToken(conn, uid)
	if err != nil {
		conn.Rollback()
		info := "生成令牌失败"
		Log("info", info)
		return errors.New(info)
	}
	err = model.InsertMgTokenLog(conn, uid, 0, tokenId, orderNo, orderType)
	if err != nil {
		conn.Rollback()
		info := "生成令牌流水失败"
		Log("info", info)
		return errors.New(info)
	}

	conn.Commit()
	return
}

//查询自有闲置令牌数量
//Auth:Xu
func GetMyselfTokenUnusedNum(uid int64) (tokenNum model.TokenNum, err error) {
	db, err := config.C.DB.DB.Begin()
	tokenNum, err = model.GetMyselfTokenUnusedNum(db, uid)
	if err != nil {
		db.Rollback()
		err = errors.New("查询自有闲置令牌失败")
		return
	}
	db.Commit()
	return

}

type TokenBuybackOrderInfo struct {
	OrderNo      string `json:"orderNo"`
	TotalPrice   int    `json:"totalPrice"`
	Num          int    `json:"num"`
	OldNum       int    `json:"oldNum"`
	NewNum       int    `json:"newNum"`
	RankNum      int    `json:"rankNum"`
	UnitPrice    int    `json:"unitPrice"`
	OldUnitPrice int    `json:"oldUnitPrice"`
	ExpireSec    int    `json:"expireSec"`
}

func GenBuybackOrder() {
	if time.Now().Hour() > 1 && time.Now().Hour() < 8 {
		return
	}
	//全网剩余可回购数量、及排队令牌数量
	restNum, rankNum, err := GetTodayRestBuyBackNum()
	if err != nil {
		return
	}
	var rate, addNum, minNum int
	if rankNum >= 0 && rankNum < 2000 {
		rate = 8
		addNum = 20
		minNum = 5
	} else if rankNum >= 2000 && rankNum < 5000 {
		if RandInt(0, 3) == 3 {
			return
		}
		rate = 7
		minNum = 5
		addNum = 16
	} else if rankNum >= 5000 && rankNum < 10000 {
		if RandInt(0, 2) == 2 {
			return
		}
		rate = 7
		minNum = 3
		addNum = 14
	} else if rankNum >= 10000 && rankNum < 15000 {
		if RandInt(0, 5) > 4 {
			return
		}
		rate = 7
		addNum = 12
	} else if rankNum >= 15000 && rankNum < 25000 {
		if RandInt(0, 5) > 1 {
			return
		}
		rate = 6
		addNum = 5
	} else {
		if RandInt(0, 5) > 2 {
			return
		}
		rate = 2
		addNum = 5
	}
	dealNum2, rankNum2, err := GetTodayRestBuyBackNum2()
	if err != nil {
		return
	}
	//干扰项
	if dealNum2 == 0 && RandInt(0, 2) == 0 {
		return
	}
	num := RandInt(minNum, addNum)
	//随机不处理  干扰项
	if num == 0 {
		return
	}

	db, err := config.C.DB.DB.Begin()
	var uid int64
	uid = 100000000
	orderNo := GenTokenOrder(9, uid)
	orderStatus := 0
	payStatus := model.TokenOrderPayed
	dealNum := 0
	if restNum > 0 {
		if restNum < TokenBuyBackMaxNum-dealNum2*rate-10 {
			db.Rollback()
			return
		}
		dealNum = num
		orderStatus = model.TokenSaleOrderStatusComplete
	} else {
		//干扰项
		if time.Now().Minute() <= 5 {
			db.Rollback()
			return
		}
		if rankNum > rankNum2*rate {
			db.Rollback()
			return
		}
		orderStatus = model.TokenSaleOrderStatusRank
		queueData := model.TokenDrawQueue{
			TokenNum:   num,
			OrderNo:    orderNo,
			Uid:        uid,
			Cvn:        0,
			CreateTime: time.Now().Unix(),
			Status:     model.TokenQueueStatusRanking,
		}
		err = model.InsertBuybackQueue(config.C.DB.DB, queueData)
		if err != nil {
			db.Rollback()
			err = errors.New("加入令牌回购排队失败")
			return
		}
	}
	//创建订单
	order := model.TokenSaleOrder{
		OrderNo:        orderNo,
		ParentOrderNo:  "",
		UnitCvntPrice:  0,
		TotalCvntPirce: 0,
		UnitUsdtPrice:  0,
		TotalUsdtPrice: 0,
		TokenNum:       num,
		BuyerUid:       0,
		DealNum:        dealNum,
		SellerUid:      uid,
		PayValidTime:   time.Now().Unix() + TokenOrderPayValidTime,
		PayStatus:      payStatus,
		Status:         orderStatus,
		OrderType:      model.TokenSaleOrderPlatBuyback,
		CreateTime:     time.Now().Unix(),
		CompleteTime:   time.Now().Unix(),
	}
	data := order
	err = model.InsertTokenSaleOrder(config.C.DB.DB, data)
	if err != nil {
		db.Rollback()
		err = errors.New("订单生成失败")
		return
	}
	db.Commit()
	return
}

//官方回购令牌下单
//Auth:Xu
func TokenBuybackCreateOrder(uid int64, num int) (err error, returnData TokenBuybackOrderInfo) {
	nxCacheKey := "cvn:PayTokenSaleOrder:" + strconv.FormatInt(uid, 10)
	res, err := redis.RedisSetnx(nxCacheKey, "1", 2)
	if err != nil || !res {
		err = errors.New("操作过于频繁，请稍后再试")
		return
	}
	//全网剩余可回购数量、及排队令牌数量
	restNum, rankNum, err := GetTodayRestBuyBackNum()
	if err != nil {
		return
	}
	//查询用户今日剩余可回购令牌数量
	userTodayRestNum, err := GetUserTodayRestBuyBackNum(uid)
	if err != nil {
		return
	}
	if userTodayRestNum <= 0 {
		err = errors.New("您今日的令牌回购数量已达上限")
		return
	}

	db, err := config.C.DB.DB.Begin()
	haveNum, err := model.GetMyselfTokenUnusedNum(db, uid)
	if err != nil {
		db.Rollback()
		err = errors.New("查询自有闲置令牌失败")
		return
	}

	if haveNum.New+haveNum.Old < num {
		db.Rollback()
		err = errors.New("可回购的闲置令牌数不足")
		return
	}

	if num > 50 {
		db.Rollback()
		err = errors.New("单次回购数量不能超过50个")
		return
	}
	err = model.UpdateTokenToLock(db, uid, num, model.TokenSaleOrderPlatBuyback)
	if err != nil {
		db.Rollback()
		return
	}
	//查询当前令牌价格
	tokenNewCvntPrice := GetCurrentTokenCvntPrice(0)
	tokenOldCvntPrice := GetCurrentTokenCvntPrice(TokenOldBatch)
	var totalCvntPirce, oldNum, newNum int
	if haveNum.Old >= num {
		oldNum = num
		totalCvntPirce = tokenOldCvntPrice * num
	} else {
		oldNum = haveNum.Old
		newNum = num - oldNum
		totalCvntPirce = tokenOldCvntPrice*oldNum + tokenNewCvntPrice*newNum
	}

	//查询最近10小时CVNT最大涨幅
	maxGain, err := GetCvntGainMax("60min", 10)
	if err != nil {
		db.Rollback()
		return
	}
	if maxGain < 0.7 || newNum > 0 {
		db.Rollback()
		err = errors.New("市场振幅过大，为保护广大矿工权益，暂时停止回购新令牌")
		//err = errors.New("CVNT市场跌幅超过10%，为保护广大矿工权益，10小时之内官方将不再回收令牌")
		return
	}

	//判断是立即处理回购，还是进入排队队列
	orderNo := GenTokenOrder(model.TokenSaleOrderPlatBuyback, uid)
	orderStatus := 0
	if restNum > 0 || uid == 8042615 || uid == 7720644 || uid == 6573444 || uid == 7294588 || uid == 8114729 || uid == 8113636 || uid == 5724782 || uid == 5512533 {
		orderStatus = model.TokenSaleOrderStatusSale
	} else {
		orderStatus = model.TokenSaleOrderStatusRank
		queueData := model.TokenDrawQueue{
			TokenNum:   num,
			OrderNo:    orderNo,
			Uid:        uid,
			Cvn:        int64(totalCvntPirce),
			CreateTime: time.Now().Unix(),
			Status:     model.TokenQueueStatusRankLocking,
		}
		err = model.InsertBuybackQueue(config.C.DB.DB, queueData)
		if err != nil {
			db.Rollback()
			err = errors.New("加入令牌回购排队失败")
			return
		}
	}

	//创建订单
	data := model.TokenSaleOrder{
		OrderNo:               orderNo,
		ParentOrderNo:         "",
		UnitCvntPrice:         tokenNewCvntPrice,
		TotalCvntPirce:        totalCvntPirce,
		UnitUsdtPrice:         0,
		TotalUsdtPrice:        0,
		TokenNum:              num,
		OldTokenNum:           oldNum,
		OldTokenUnitCvntPrice: tokenOldCvntPrice,
		BuyerUid:              0,
		SellerUid:             uid,
		PayValidTime:          time.Now().Unix() + TokenOrderPayValidTime,
		Status:                orderStatus,
		OrderType:             model.TokenSaleOrderPlatBuyback,
		CreateTime:            time.Now().Unix(),
	}
	err = model.InsertTokenSaleOrder(config.C.DB.DB, data)
	if err != nil {
		db.Rollback()
		err = errors.New("订单生成失败")
		return
	}
	db.Commit()

	returnData = TokenBuybackOrderInfo{
		OrderNo:      data.OrderNo,
		TotalPrice:   data.TotalCvntPirce,
		OldNum:       oldNum,
		NewNum:       newNum,
		RankNum:      rankNum,
		Num:          num,
		UnitPrice:    data.UnitCvntPrice,
		OldUnitPrice: tokenOldCvntPrice,
		ExpireSec:    TokenOrderPayValidTime,
	}
	//延时释放令牌
	go func(orderNo string) {
		time.Sleep(time.Duration(TokenOrderPayValidTime) * time.Second)
		orderInfo, _ := model.GetTokenOrderInfoByNo(config.C.DB.DB, orderNo)
		if orderInfo.Status == model.TokenSaleOrderStatusComplete || orderInfo.PayStatus == model.TokenOrderPayed {
			return
		}
		//SetTokenSaleOverdueCount(orderInfo.BuyerUid)
		db, _ := config.C.DB.DB.Begin()
		err := model.UpdateTokenSaleOrderStatus(db, orderNo, model.TokenSaleOrderStatusOverdue)
		if err != nil {
			db.Rollback()
			return
		}
		err = model.UpdateNewOldTokenToUnused(db, orderInfo.SellerUid, orderInfo.OldTokenNum, orderInfo.TokenNum-orderInfo.OldTokenNum, model.TokenSaleOrderPlatBuyback)
		if err != nil {
			db.Rollback()
			return
		}
		if orderInfo.Status == model.TokenSaleOrderStatusRank {
			err := model.UpdateTokenDrawQueueStatus(db, orderNo, model.TokenQueueStatusExpire)
			if err != nil {
				db.Rollback()
				return
			}
		}
		db.Commit()

	}(data.OrderNo)

	return
}

//过期订单定时处理任务
//Auth:Xu
func TokenOrderOverdueDeal() {
	orders, _ := model.SelectOverdueTokenOrder(config.C.DB.DB)
	for _, order := range orders {
		switch order.OrderType {
		//官方渠道购买令牌订单
		case model.TokenSaleOrderPlatBuy:
			go func(order model.TokenSaleOrder) {
				err := model.UpdateTokenSaleOrderStatus(config.C.DB.DB, order.OrderNo, model.TokenSaleOrderStatusOverdue)
				if err != nil {
					Log("info", "修改支付超时订单失败:"+order.OrderNo)
				}
			}(order)
		//第三方购买订单
		case model.TokenSaleOrderOutBuy:
			go func(order model.TokenSaleOrder) {
				db, _ := config.C.DB.DB.Begin()
				err := model.UpdateTokenSaleOrderStatus(db, order.OrderNo, model.TokenSaleOrderStatusOverdue)
				if err != nil {
					db.Rollback()
					return
				}
				err = model.UpdateSaleOrderLockNum(db, order.ParentOrderNo, -order.TokenNum)
				if err != nil {
					db.Rollback()
					return
				}
				db.Commit()
			}(order)

		//官方回购令牌订单
		case model.TokenSaleOrderPlatBuyback:
			go func(order model.TokenSaleOrder) {
				db, _ := config.C.DB.DB.Begin()

				err := model.UpdateTokenSaleOrderStatus(db, order.OrderNo, model.TokenSaleOrderStatusOverdue)
				if err != nil {
					db.Rollback()
					return
				}
				err = model.UpdateNewOldTokenToUnused(db, order.SellerUid, order.OldTokenNum, order.TokenNum-order.OldTokenNum, model.TokenSaleOrderPlatBuyback)
				if err != nil {
					db.Rollback()
					return
				}
				if order.Status == model.TokenSaleOrderStatusRank {
					err := model.UpdateTokenDrawQueueStatus(db, order.OrderNo, model.TokenQueueStatusExpire)
					if err != nil {
						db.Rollback()
						return
					}
				}
				db.Commit()
			}(order)
		}

	}
}

type BuybackOrderSubmitReturnInfo struct {
	OrderNo    string `json:"orderNo"`
	TotalPrice int    `json:"totalPrice"`
	Num        int    `json:"num"`
	UnitPrice  int    `json:"unitPrice"`
}

//令牌回购订单提交
//Auth:Xu
func BuybackOrderSubmit(orderNo string, uid int64) (returnData BuybackOrderSubmitReturnInfo, err error) {
	orderInfo, _ := model.GetTokenOrderInfoByNo(config.C.DB.DB, orderNo)
	if orderInfo.Status == model.TokenSaleOrderStatusOverdue || orderInfo.PayValidTime < time.Now().Unix() {
		err = errors.New("回购订单已超时，请重新操作")
		return
	}
	if orderInfo.SellerUid != uid {
		err = errors.New("非法操作")
		return
	}
	conn, _ := config.C.DB.DB.Beginx()
	tokenIds, err := model.GetMyselfTokenLockingNum(config.C.DB.DB, uid, orderInfo.TokenNum, model.TokenSaleOrderPlatBuyback)
	if err != nil || len(tokenIds) < orderInfo.TokenNum {
		conn.Rollback()
		err = errors.New("订单异常")
		return
	}
	//排队中的订单，暂时不处理
	if orderInfo.Status == model.TokenSaleOrderStatusRank {
		err = model.UpdateTokenDrawQueueStatus(conn, orderInfo.OrderNo, model.TokenQueueStatusRanking)
		if err != nil {
			conn.Rollback()
			return
		}
		//订单确认完成
		err = model.UpdateTokenSaleOrderBuybackRankConfirm(conn, orderNo)
		if err != nil {
			conn.Rollback()
			err = errors.New("修改订单状态失败")
			return
		}

		conn.Commit()
		return
	}
	err = model.UpdateTokenSaleOrderBuybackComplete(conn, orderNo, orderInfo.TokenNum)
	if err != nil {
		conn.Rollback()
		err = errors.New("修改订单状态失败")
		return
	}
	rowNums, err := model.UpdateTokenToRecycle(conn, uid, tokenIds)
	if err != nil || int(rowNums) != len(tokenIds) {
		conn.Rollback()
		err = errors.New("修改令牌状态失败")
		return
	}
	for _, tokenId := range tokenIds {
		err = model.InsertMgTokenLog(conn, 0, uid, tokenId, orderNo, model.TokenLogTypeBuyback)
		if err != nil {
			conn.Rollback()
			err = errors.New("生成令牌流水失败")
			return
		}
	}

	// 提取cvnt start   锁仓时间72小时
	err = PledgeDraw(conn, uid, float64(orderInfo.TotalCvntPirce), model.PledgeToken, 0)
	if err != nil {
		conn.Rollback()
		return
	}
	//提取cvnt end

	conn.Commit()

	returnData = BuybackOrderSubmitReturnInfo{
		OrderNo:    orderNo,
		TotalPrice: orderInfo.TotalCvntPirce,
		Num:        orderInfo.TokenNum,
		UnitPrice:  orderInfo.UnitCvntPrice,
	}

	return
}

// 判断租借人保证金是否充足
// Auth: hzp
// needTokenPledgingCvnFloat64 不可提取的保证金数
func CheckRentTokenPledgeStatus(Uid int64, need int) (res model.CheckRentTokenData, err error) {

	res.EnoughDays = -1
	// status 1-成功 0-失败 2-保证金不足 3-余额不足
	// 初始化状态为1 成功
	res.Status = 1
	// 已租借令牌数
	res.Rent, err = model.GetMgTokenInfoByUserId(Uid)
	if err != nil {
		res.Status = 0
		return res, err
	}

	// 求租借令牌数
	res.UnRent, err = model.GetMgTokenSellerOrderByUserId(Uid)
	if err != nil {
		res.Status = 0
		return res, err
	}

	// 租借的令牌数
	rentTokenNum := res.Rent + res.UnRent
	// 基数
	if need > 0 {
		rentTokenNum = rentTokenNum + need
	}
	if rentTokenNum <= 0 {
		res.Status = 1
		return res, nil
	}

	// 需要缴纳的保证金
	needTokenPledgingCvn := strconv.Itoa(rentTokenNum * TokenPledgingNum)
	res.TotalCvn, _ = strconv.ParseFloat(needTokenPledgingCvn, 64)
	rentTokenNumFloat64, _ := strconv.ParseFloat(strconv.Itoa(rentTokenNum), 64)

	// 用户已有可用余额 信息
	userAmount, err := GetUserCvnAmountByUid(Uid)
	if err != nil {
		res.Status = 0
		return res, err
	}

	// 用户已有保证金 信息
	var frozenBalanceAmount float64
	frozenAmount, err := model.GetCvnFrozenInfo(Uid, model.PledgeDeposit)
	if err == sql.ErrNoRows {
		frozenBalanceAmount = 0.00
	} else if err != nil {
		res.Status = 0
		return res, err
	} else {
		frozenBalanceAmount = frozenAmount.Amount
	}

	Log("info", "已有保证金：", frozenBalanceAmount)
	res.EnoughDays = int(math.Floor(frozenBalanceAmount / (TokenPledgingNumPres * rentTokenNumFloat64)))
	Log("info", "剩余保证金够发：", res.EnoughDays, " 天")
	// 已有保证金  frozenAmount.Amount
	// 需要保证金总数  res.TotalCvn
	// 扣除保证金之后的可用余额  res.Cvn
	// 扣除已有保证之后需要补交  res.NeedCvn
	if frozenBalanceAmount >= res.TotalCvn {
		res.Status = 1
		res.NeedCvn = 0.00
		res.Cvn = userAmount.Balance
	} else if frozenBalanceAmount < res.TotalCvn {
		// 保证金充足不需要补充
		res.Status = 2
		res.NeedCvn = res.TotalCvn - frozenBalanceAmount
		res.Cvn = frozenBalanceAmount + userAmount.Balance - res.TotalCvn
		Log("info", "保证金不足需要补缴：", res.NeedCvn)
	}

	return res, nil
}

//发布令牌出售订单
//Auth:Xu
func IssueTokenUsdtOrder(uid int64, num int, uintPrice float64) (orderNo string, err error) {
	//查询单价是否低于常规价的60%
	if uintPrice < 50 {
		err = errors.New("销售单价不能低于50Usdt")
	}
	//查询自有闲置令牌数
	db, err := config.C.DB.DB.Begin()
	tokenNum, err := model.GetMyselfTokenUnusedNum(db, uid)
	if err != nil {
		db.Rollback()
		err = errors.New("查询自有闲置令牌失败")
		return
	}
	if tokenNum.New < num {
		db.Rollback()
		err = errors.New("可出售令牌数不足")
		return
	}
	//发布订单、查询今天总发布的订单数，以及当前挂售的订单数量
	salingNum, err := model.GetCurrentUsdtOrderNumSelf(uid)
	if err != nil {
		db.Rollback()
		err = errors.New("查询正在挂售中的订单数失败")
		return
	}
	if salingNum >= TokenUsdtOrderMaxNum {
		err = errors.New("最大允许的挂售订单数不能超过3单")
		db.Rollback()
		return
	}
	//今日累计发布的订单数不能超过限制
	toadyNum, err := model.GetTodayUsdtOrderNumSelf(uid)
	if err != nil {
		db.Rollback()
		err = errors.New("查询今日发布的订单数失败")
		return
	}
	if toadyNum >= TokenUsdtOrderMaxNum {
		err = errors.New("今日发布订单数已超过3单，已达上限")
		db.Rollback()
		return
	}

	//创建订单
	data := model.TokenSaleOrder{
		OrderNo:        GenTokenOrder(model.TokenSaleOrderPlatBuyback, uid),
		ParentOrderNo:  "",
		UnitCvntPrice:  0,
		TotalCvntPirce: 0,
		UnitUsdtPrice:  uintPrice,
		TotalUsdtPrice: uintPrice * float64(num),
		TokenNum:       num,
		BuyerUid:       0,
		SellerUid:      uid,
		PayValidTime:   0,
		Status:         model.TokenSaleOrderStatusSale,
		OrderType:      model.TokenSaleOrderOutSell,
		CreateTime:     time.Now().Unix(),
	}
	err = model.InsertTokenSaleOrder(db, data)
	if err != nil {
		db.Rollback()
		err = errors.New("订单生成失败")
		return
	}
	//锁定令牌
	err = model.UpdateTokenToLock(db, uid, num, model.TokenSaleOrderOutSell)
	if err != nil {
		db.Rollback()
		err = errors.New("锁定令牌失败")
		return
	}
	db.Commit()
	orderNo = data.OrderNo
	return
}

type TokenUsdtOrderInfo struct {
	OrderNo    string  `json:"orderNo"`
	TotalPrice string  `json:"totalPrice"`
	Num        int     `json:"num"`
	UnitPrice  float64 `json:"unitPrice"`
}

//查询出售订单的详情
//Auth: Xu
func GetOrderInfo(orderNo string, uid int64) (returnData TokenUsdtOrderInfo, err error) {
	orderInfo, err := model.GetTokenOrderInfoByNo(config.C.DB.DB, orderNo)
	if err != nil {
		err = errors.New("查询订单详情失败")
		return
	}
	if orderInfo.SellerUid != uid {
		err = errors.New("非法操作")
		return
	}
	returnData = TokenUsdtOrderInfo{
		OrderNo:    orderNo,
		TotalPrice: strconv.FormatFloat(orderInfo.TotalUsdtPrice, 'f', 2, 64),
		Num:        orderInfo.TokenNum,
		UnitPrice:  orderInfo.UnitUsdtPrice,
	}

	return
}

//查询平台在售的订单列表
//Auth:Xu
func GetSalingOrderList(per, page int) (returnData []TokenUsdtOrderInfo, err error) {
	orderList, err := model.GetTokenUsdtOrderList(per, page)
	if err != nil {
		err = errors.New("查询订单出错")
		return
	}
	for _, order := range orderList {
		var data TokenUsdtOrderInfo
		data.OrderNo = order.OrderNo
		data.UnitPrice = order.UnitUsdtPrice
		data.Num = order.TokenNum - order.DealNum - order.LockNum
		data.TotalPrice = strconv.FormatFloat(float64(data.Num)*data.UnitPrice, 'f', 2, 64)
		returnData = append(returnData, data)
	}

	return
}

//查询平台在售的订单列表
//Auth:Xu
func GetMyBuybackQueueList(uid int64) (returnData []model.TokenDrawQueueReturn, err error) {
	returnData, err = model.GetMyBuybackQueue(config.C.DB.DB, uid)
	if err != nil {
		err = errors.New("查询排队列表出错")
		return
	}

	return
}

type OutsideTradeCreateOrderInfo struct {
	OrderNo    string  `json:"orderNo"`
	TotalPrice float64 `json:"totalPrice"`
	Num        int     `json:"num"`
	UnitPrice  float64 `json:"unitPrice"`
	UsableUsdt float64 `json:"usableUsdt"`
	ExpireSec  int     `json:"expireSec"`
}

//第三方渠道购买令牌下单
//Auth:Xu
func OutsideTradeCreateOrder(orderNo string, num int, buyerUid int64) (returnOrderInfo OutsideTradeCreateOrderInfo, err error) {
	//查询每日购买次数限制
	cacheKey := GetTokenSaleOverdueCountCacheKey(buyerUid)
	if redis.Exists(cacheKey) {
		overdueNumStr, _ := redis.Get(cacheKey)
		overdueNum, _ := strconv.Atoi(overdueNumStr)
		if overdueNum > TokenSaleOrderOverdueMaxNum {
			err = errors.New("今日逾期未支付订单数已达3次")
			return
		}
	}
	//查询订单可出售令牌数量
	parentOrderInfo, err := model.GetTokenOrderInfoByNo(config.C.DB.DB, orderNo)
	if num > parentOrderInfo.TokenNum-parentOrderInfo.DealNum-parentOrderInfo.LockNum {
		err = errors.New("订单可出售令牌数不足，请重新下单")
		return
	}
	if parentOrderInfo.SellerUid == buyerUid {
		err = errors.New("不允许购买自己的令牌")
		return
	}
	if parentOrderInfo.Status != model.TokenSaleOrderStatusSale {
		err = errors.New("订单已撤销")
		return
	}
	//查询账户USDT余额
	usdtAmount, err := GetUserUsdtAmountByUid(buyerUid)
	totalPrice, _ := strconv.ParseFloat(strconv.FormatFloat(parentOrderInfo.UnitUsdtPrice*float64(num), 'f', 2, 64), 64)
	if usdtAmount.Balance < totalPrice {
		err = errors.New("账户可用USDT余额不足")
		return
	}
	//下单，锁定出售订单的令牌数
	db, err := config.C.DB.DB.Begin()
	data := model.TokenSaleOrder{
		OrderNo:        GenTokenOrder(model.TokenSaleOrderOutBuy, buyerUid),
		ParentOrderNo:  orderNo,
		UnitCvntPrice:  0,
		TotalCvntPirce: 0,
		UnitUsdtPrice:  parentOrderInfo.UnitUsdtPrice,
		TotalUsdtPrice: totalPrice,
		TokenNum:       num,
		BuyerUid:       buyerUid,
		SellerUid:      parentOrderInfo.SellerUid,
		Status:         model.TokenSaleOrderStatusSale,
		PayValidTime:   time.Now().Unix() + TokenUsdtOrderPayValidTime,
		OrderType:      model.TokenSaleOrderOutBuy,
		CreateTime:     time.Now().Unix(),
	}
	err = model.InsertTokenSaleOrder(db, data)
	if err != nil {
		db.Rollback()
		err = errors.New("订单生成失败")
		return
	}
	err = model.UpdateSaleOrderLockNum(db, orderNo, num)
	if err != nil {
		db.Rollback()
		err = errors.New("锁定订单令牌数失败")
		return
	}
	db.Commit()
	//延时释放订单锁定令牌数
	//延时释放令牌
	go func(orderNo string) {
		time.Sleep(time.Duration(TokenUsdtOrderPayValidTime) * time.Second)
		orderInfo, err := model.GetTokenOrderInfoByNo(config.C.DB.DB, orderNo)
		if orderInfo.PayStatus == model.TokenOrderPayed {
			return
		}
		SetTokenSaleOverdueCount(orderInfo.BuyerUid)
		db, _ := config.C.DB.DB.Begin()
		err = model.UpdateTokenSaleOrderStatus(db, orderNo, model.TokenSaleOrderStatusOverdue)
		if err != nil {
			db.Rollback()
			return
		}
		err = model.UpdateSaleOrderLockNum(db, orderInfo.ParentOrderNo, -orderInfo.TokenNum)
		if err != nil {
			db.Rollback()
			return
		}
		db.Commit()

	}(data.OrderNo)

	returnOrderInfo = OutsideTradeCreateOrderInfo{
		OrderNo:    data.OrderNo,
		TotalPrice: data.TotalUsdtPrice,
		Num:        data.TokenNum,
		UnitPrice:  data.UnitUsdtPrice,
		UsableUsdt: usdtAmount.Balance,
		ExpireSec:  TokenUsdtOrderPayValidTime,
	}
	return
}

//增加第三方购买令牌订单超时次数缓存
//Auth:Xu
func SetTokenSaleOverdueCount(uid int64) {
	cacheKey := GetTokenSaleOverdueCountCacheKey(uid)
	if _, err := redis.Incr(cacheKey); err != nil {
		Log("err", "递增逾期未支付订单数失败")
	}
	expireTimeSec, _ := GetNowToNextDayUnix()
	redis.Expire(cacheKey, int(expireTimeSec))
}

//第三方渠道购买令牌订单支付
//Auth:Xu
func OutsideTradePay(orderNo string, uid int64) (err error) {
	//查询购买人
	orderInfo, err := model.GetTokenOrderInfoByNo(config.C.DB.DB, orderNo)
	if orderInfo.BuyerUid != uid {
		err = errors.New("非法操作")
		return
	}
	//查询支付时效
	if orderInfo.Status == model.TokenSaleOrderStatusOverdue || orderInfo.PayValidTime < time.Now().Unix() {
		err = errors.New("订单已超时")
		return
	}
	//查询usdt余额
	usdtAmount, err := GetUserUsdtAmountByUid(uid)
	if usdtAmount.Balance < orderInfo.TotalUsdtPrice {
		err = errors.New("账户可用USDT余额不足")
		return
	}
	parentOrderInfo, err := model.GetTokenOrderInfoByNo(config.C.DB.DB, orderInfo.ParentOrderNo)

	//支付
	var data model.UsdtLog
	data.Uid = uid
	data.Amount = -orderInfo.TotalUsdtPrice
	data.Balance = usdtAmount.Balance
	data.TokenOrderId = orderInfo.Id

	db, err := config.C.DB.DB.Begin()

	//扣取账户内的usdt
	//err = UpdateUsdtAmount(db, uid, usdtAmount.Balance, -orderInfo.TotalUsdtPrice, model.LogSourceUsdtBuyToken, orderInfo.Id)
	if err != nil {
		db.Rollback()
		return
	}
	//查询卖方信息, 将扣取的usdt转账给卖方
	//sellerUsdtAmount, err := GetUserUsdtAmountByUid(orderInfo.SellerUid)
	//err = UpdateUsdtAmount(db, orderInfo.SellerUid, sellerUsdtAmount.Balance, orderInfo.TotalUsdtPrice, model.LogSourceUsdtSaleToken, orderInfo.Id)
	if err != nil {
		db.Rollback()
		return
	}
	//释放销售订单的令牌锁定数、生成令牌流水、 修改令牌归属人
	err = model.UpdateBuyOrderPayComplete(db, orderNo)
	if err != nil {
		err = errors.New("购买订单完成异常")
		db.Rollback()
		return
	}
	parentOrderTokenNumDue := parentOrderInfo.TokenNum - parentOrderInfo.DealNum
	if orderInfo.TokenNum > parentOrderTokenNumDue {
		db.Rollback()
		err = errors.New("订单令牌异常")
		return
	}
	//令牌全部售完， 则修改销售订单为 已完成
	if orderInfo.TokenNum == parentOrderTokenNumDue {
		err = model.UpdateSaleOrderComplete(db, parentOrderInfo.OrderNo, orderInfo.TokenNum)
		if err != nil {
			err = errors.New("销售订单完成异常")
			db.Rollback()
			return
		}
	} else {
		err = model.UpdateSaleOrderDealNum(db, parentOrderInfo.OrderNo, orderInfo.TokenNum)
		if err != nil {
			err = errors.New("修改父订单状态异常")
			db.Rollback()
			return
		}
	}
	//查询自有锁定中的令牌
	tokenIds, err := model.GetMyselfTokenLockingNum(config.C.DB.DB, orderInfo.SellerUid, orderInfo.TokenNum, model.TokenSaleOrderOutSell)
	if err != nil || len(tokenIds) < orderInfo.TokenNum {
		db.Rollback()
		err = errors.New("订单异常")
		return
	}
	//修改令牌归属信息
	rowNums, err := model.UpdateTokenBelong(db, uid, tokenIds)
	if err != nil || int(rowNums) != len(tokenIds) {
		db.Rollback()
		err = errors.New("修改令牌归属失败")
		return
	}
	//生成令牌流水日志
	for _, tokenId := range tokenIds {
		err = model.InsertMgTokenLog(db, uid, orderInfo.SellerUid, tokenId, orderNo, model.TokenLogTypeSale)
		if err != nil {
			db.Rollback()
			err = errors.New("生成令牌流水失败")
			return
		}
	}

	db.Commit()

	return
}

//出售订单撤销
//Auth:Xu
func SaleOrderCancel(orderNo string, uid int64) (err error) {
	orderInfo, err := model.GetTokenOrderInfoByNo(config.C.DB.DB, orderNo)
	if orderInfo.SellerUid != uid || orderInfo.OrderType != model.TokenSaleOrderOutSell {
		err = errors.New("非法操作")
		return
	}
	if orderInfo.Status != model.TokenSaleOrderStatusSale {
		err = errors.New("订单状态异常")
		return
	}
	if orderInfo.LockNum > 0 {
		err = errors.New("订单锁定中,请稍后再试")
		return
	}
	db, err := config.C.DB.DB.Begin()
	err = model.UpdateSaleOrderCancel(db, orderNo)
	if err != nil {
		db.Rollback()
		err = errors.New("订单状态修改失败")
		return
	}
	err = model.UpdateNewOldTokenToUnused(db, uid, orderInfo.TokenNum-orderInfo.DealNum, 0, model.TokenSaleOrderOutSell)
	if err != nil {
		db.Rollback()
		err = errors.New("订单令牌状态修改失败")
		return
	}
	db.Commit()

	return
}

type mySaleOrderListOrderInfo struct {
	OrderNo    string  `json:"orderNo"`
	TotalPrice float64 `json:"totalPrice"`
	Num        int     `json:"num"`
	UnitPrice  float64 `json:"unitPrice"`
	CreateTime string  `json:"createTime"`
	SellOut    int     `json:"sellOut"`
	Status     int     `json:"status"`
}

//查询我的出售订单列表
//Auth:Xu
func GetMySaleOrderList(uid int64, per, page int) (returnList []mySaleOrderListOrderInfo, err error) {
	list, err := model.GetMySaleOrderList(uid, per, page)
	if err != nil {
		err = errors.New("查询订单列表失败")
		return
	}
	for _, order := range list {
		var returnOrder mySaleOrderListOrderInfo
		createTime := time.Unix(order.CreateTime, 0)
		returnOrder.OrderNo = order.OrderNo
		returnOrder.TotalPrice = order.TotalUsdtPrice
		returnOrder.Num = order.TokenNum
		returnOrder.UnitPrice = order.UnitUsdtPrice
		returnOrder.CreateTime = createTime.Format("2006") + "-" + createTime.Format("01") + "-" + createTime.Format("02") + "/" + " " + createTime.Format("15") + ":" + createTime.Format("04")
		returnOrder.SellOut = order.DealNum
		returnOrder.Status = order.Status
		returnList = append(returnList, returnOrder)
	}

	return

}

//令牌广场租借列表
//Auth:Xu
func GetRentList(per, page int) (orderList []model.RentListInfo, err error) {
	average := GetNetAverageIncome(model.GlobalNetAverageIncome)

	orderList, err = model.GetRentOrderList(per, page, average)
	if err != nil {
		return
	}

	return
}

//令牌广场租借列表
//Auth:Xu
func GetMyRentList(uid int64, per, page int) (orderList []model.RentListInfo, err error) {
	orderList, err = model.GetMyRentOrderList(uid, per, page)
	if err != nil {
		err = errors.New("查询租赁订单出错")
	}

	return
}

//令牌广场租借列表
//Auth:Xu
func GetMyRentOrderInfo(uid int64, orderNo string) (orderInfo model.RentListInfo, err error) {
	orderInfo, err = model.GetMyRentOrderInfo(orderNo)
	if err != nil {
		err = errors.New("未查询到订单详情")
	}

	return
}

//获取违规次数
//Author: TXG
func GetBadRecords(uid int64) (num int64, err error) {
	miner, err := model.GetMinerInfoByUid(uid)
	if err != nil {
		err = errno.GetDataErr
		return
	}
	num = miner.BadRecords
	return
}

//令牌租赁-发布订单
//Author: TXG
func RentSubmitOrder(uid int64, postData model.RentSubmitOrderRequest) (orderNo string, err error) {
	var (
		data model.TokenSellOrder
	)
	//参数校验
	if postData.Num <= 0 || postData.Days <= 0 || postData.Scale <= 0 {
		err = errno.ParamsError
		return
	}
	if postData.Days < 5 {
		err = errno.RentLeastDayErr
		return
	}
	if postData.Days > 100 {
		err = errno.RentMostDayErr
		return
	}
	//检查违规记录
	BadRecords, err := GetBadRecords(uid)
	if err != nil {
		err = errno.GetDataErr
		return
	}
	if BadRecords >= 3 {
		err = errno.BadRecordsErr
		return
	}
	//检查保证金
	checkData, err := CheckRentTokenPledgeStatus(uid, int(postData.Num))
	if err != nil {
		return orderNo, err
	}
	if checkData.Status == 2 {
		return orderNo, errno.PledgedUnEnoughErr
	}
	//检查是否绑定手机号
	userMobile, err := model.GetUserMobile(config.C.DB.DB, uid)
	if err != nil {
		err = errno.GetDataErr
		return
	}
	if userMobile.Area == 0 || userMobile.Mobile == "" {
		err = errno.NotBindMobile
		return
	}
	//校验短信验证码
	if errValid := ValidateCode(userMobile.Mobile, SendCodeSceneRentSubmitOrder, postData.Code); errValid != nil {
		err = errValid
		return
	}
	//检查支付密码是否校验成功
	if ok := IsPayPasswordChecked(uid, postData.U); !ok {
		err = errno.PayPasswordCheckExpire
		return
	}
	DeleteU(uid, postData.U)
	nowTime := time.Now().Unix()
	data.OrderNo = GenTokenOrder(model.TokenSellOrderRentSeeking, uid)
	data.TokenNum = postData.Num
	data.LendUid = uid
	data.Scale = postData.Scale
	data.Days = postData.Days
	data.Status = model.TokenSellOrderStatusRenting
	data.Type = model.TokenSellOrderRentSeeking
	data.Info = template.HTMLEscapeString(postData.Info)
	data.CreateTime = nowTime
	err = model.AddMgTokenSellOrder(config.C.DB.DB, data)
	if err != nil {
		err = errno.UpdateDataErr
		return
	}
	return data.OrderNo, nil
}

//令牌租赁-取消订单
//Author: TXG
func RentCancelOrder(uid int64, orderNo string) (ok bool, err error) {
	var (
		status int64
	)
	order, err := model.GetMgTokenSellOrder(config.C.DB.DB, orderNo)
	if err != nil {
		err = errno.GetDataErr
		return false, err
	}
	if order.LendUid != uid {
		err = errno.IllegalErr
		return false, err
	}
	if order.Status != 1 {
		err = errno.CanNotCancelErr
		return false, err
	}
	if order.DealNum > 0 {
		status = 2
	} else {
		status = 3
	}
	if order.TokenNum == order.DealNum {
		err = errno.CanNotCancelErr
		return false, err
	}
	err = model.UpdateMgTokenSellOrderStatus(config.C.DB.DB, orderNo, status)
	if err != nil {
		err = errno.UpdateDataErr
		return false, err
	}
	return true, nil
}

//令牌租赁-出租令牌
//Author: TXG
func RentToken(uid int64, postData model.RentToken) (orderNo string, err error) {
	var (
		data model.TokenSellOrder
	)
	if postData.Num <= 0 {
		err = errno.ParamsError
		return
	}
	//获取订单信息
	order, err := model.GetMgTokenSellOrder(config.C.DB.DB, postData.OrderNo)
	if err != nil {
		err = errno.GetDataErr
		return
	}
	if order.Status != 1 {
		err = errno.CanNotRentErr
		return
	}
	if postData.Num > order.TokenNum-order.DealNum {
		err = errno.TooManyRentErr
		return
	}
	if order.LendUid == uid {
		err = errno.RentToSelfErr
		return
	}
	//校验闲置令牌是否足够
	idleTokenNum, err := model.GetIdleTokenNum(config.C.DB.DB, uid)
	if err != nil {
		err = errno.GetDataErr
		return
	}
	if idleTokenNum < postData.Num {
		err = errno.TokenNotEnoughErr
		return
	}
	//检查是否绑定手机号
	userMobile, err := model.GetUserMobile(config.C.DB.DB, uid)
	if err != nil {
		err = errno.GetDataErr
		return
	}
	if userMobile.Area == 0 || userMobile.Mobile == "" {
		err = errno.NotBindMobile
		return
	}
	//校验短信验证码
	if errValid := ValidateCode(userMobile.Mobile, SendCodeSceneRentToken, postData.Code); errValid != nil {
		err = errValid
		return
	}
	//检查支付密码是否校验成功
	if ok := IsPayPasswordChecked(uid, postData.U); !ok {
		err = errno.PayPasswordCheckExpire
		return
	}
	DeleteU(uid, postData.U)
	//提交事务
	nowTime := time.Now().Unix()
	data.OrderNo = GenTokenOrder(model.TokenSellOrderRentOut, uid)
	data.ParentOrderNo = postData.OrderNo
	data.TokenNum = postData.Num
	data.DealNum = postData.Num
	data.RentUid = uid
	data.LendUid = order.LendUid
	data.Scale = order.Scale
	data.Days = order.Days
	data.EndTime = nowTime + TimeFormatDay*data.Days
	data.Status = model.TokenSellOrderStatusDeal
	data.Type = model.TokenSellOrderRentOut
	data.CreateTime = nowTime
	data.CompleteTime = nowTime
	ok, err := model.RentTrans(config.C.DB.DB, data)
	if err != nil || !ok {
		err = errno.UpdateDataErr
		return
	}
	return data.OrderNo, nil
}

//令牌租赁-检查保证金
//Author: TXG
func RentCheckDeposit(uid int64, num int64) (ok bool, data model.CheckCheckDepositRes, err error) {
	// 检查保证金
	checkData, err := CheckRentTokenPledgeStatus(uid, int(num))
	if err != nil {
		return false, data, err
	}
	if checkData.Status == 2 {
		data.NeedCvnt = checkData.NeedCvn
		data.TokenNum = num
		data.Day = TokenPledgingDay
		data.UnitCvnt = TokenPledgingCvnt
		return false, data, nil
	}
	return true, data, err
}

func NotRentNum(uid int64) (num int64, err error) {
	num, err = model.GetNotRentNum(config.C.DB.DB, uid)
	if err != nil {
		err = errno.GetDataErr
		return 0, err
	}
	return num, err
}

//令牌回购排队队列撤销
//Auth:Xu
func BuybackQueueCancel(uid int64, orderNo string) (err error) {
	orderInfo, _ := model.GetTokenOrderInfoByNo(config.C.DB.DB, orderNo)
	if orderInfo.SellerUid != uid {
		err = errors.New("非法操作")
		return
	}
	if orderInfo.Status != model.TokenSaleOrderStatusRank {
		err = errors.New("回购订单状态异常")
		return
	}
	//SetTokenSaleOverdueCount(orderInfo.BuyerUid)
	db, _ := config.C.DB.DB.Begin()
	err = model.UpdateTokenSaleOrderStatus(db, orderNo, model.TokenSaleOrderStatusCancel)
	if err != nil {
		db.Rollback()
		return
	}
	err = model.UpdateNewOldTokenToUnused(db, orderInfo.SellerUid, orderInfo.OldTokenNum, orderInfo.TokenNum-orderInfo.OldTokenNum, model.TokenSaleOrderPlatBuyback)
	if err != nil {
		db.Rollback()
		return
	}
	if orderInfo.Status == model.TokenSaleOrderStatusRank {
		err = model.UpdateTokenDrawQueueStatus(db, orderNo, model.TokenQueueStatusExpire)
		if err != nil {
			db.Rollback()
			return
		}
	}
	db.Commit()
	return
}

//每天10点定时处理令牌回购排队队列
//Auth:Xu
func DealBuybackQueue() {
	//每天10点以后执行
	if time.Now().Hour() < 10 {
		return
	}
	todayDealNum, err := model.GetTodayBuyBackNum(config.C.DB.DB)
	if err != nil {
		Log("err", err.Error())
		return
	}
	waitNum := TokenBuyBackMaxNum - todayDealNum
	if waitNum <= 0 {
		return
	}
	queueList, err := model.GetBuybackQueueBefore(config.C.DB.DB, waitNum)
	if err != nil {
		Log("err", err.Error())
		return
	}
	for _, draw := range queueList {
		go func(task model.TokenDrawQueueBefore) {
			conn, _ := config.C.DB.DB.Beginx()
			tokenIds, err := model.GetMyselfTokenLockingNum(config.C.DB.DB, task.Uid, task.TokenNum, model.TokenSaleOrderPlatBuyback)
			if task.Uid != 100000000 {
				if err != nil || len(tokenIds) < task.TokenNum {
					conn.Rollback()
					return
				}
			}
			err = model.UpdateTokenSaleOrderBuybackComplete(conn, task.OrderNo, task.TokenNum)
			if err != nil {
				conn.Rollback()
				err = errors.New("修改订单状态失败")
				return
			}

			//修改队列任务状态
			err = model.UpdateTokenDrawQueueStatus(conn, task.OrderNo, model.TokenQueueStatusComplete)
			if err != nil {
				conn.Rollback()
				return
			}
			if task.Uid == 100000000 {
				conn.Commit()
				return
			}

			rowNums, err := model.UpdateTokenToRecycle(conn, task.Uid, tokenIds)
			if err != nil || int(rowNums) != len(tokenIds) {
				conn.Rollback()
				err = errors.New("修改令牌状态失败")
				return
			}
			for _, tokenId := range tokenIds {
				err = model.InsertMgTokenLog(conn, 0, task.Uid, tokenId, task.OrderNo, model.TokenLogTypeBuyback)
				if err != nil {
					conn.Rollback()
					err = errors.New("生成令牌流水失败")
					return
				}
			}

			err = PledgeDraw(conn, task.Uid, float64(task.Cvn), model.PledgeToken, 0)
			if err != nil {
				conn.Rollback()
				return
			}
			//提取cvnt end

			conn.Commit()
		}(draw)
	}
}

//获取矿工平均收益
//Author: TXG
func MinerAverageRevenue(uid int64) (averageRevenue float64, err error) {
	averageRevenue, err = model.GetMinerIncomeAbility(config.C.DB.DB, uid)
	if err != nil {
		err = errno.GetDataErr
		return 0, err
	}
	return
}

func CheckDraw() {
	TaskList, _ := model.FixedUnDoTask()
	//cacheKey := UserRedisPrefix + RedisTokenDrawListKey
	for _, k := range TaskList {
		conn, _ := config.C.DB.DB.Beginx()
		////更新不可用资产表信息
		//err := UpdateCvnFrozenAmount(conn, k.Uid, 0, -k.Cvn, 4)
		//if err != nil {
		//	Log("err", err.Error())
		//	conn.Rollback()
		//	return
		//}

		////更新redis
		//InsIdStr := strconv.FormatInt(k.TaskId, 10)
		//_, err = redis.ZAdd(cacheKey, InsIdStr, k.Time)
		//if err != nil {
		//	conn.Rollback()
		//	return
		//}

		updateAmountData := UpdateCvnAmountParams{
			Uid:          k.Uid,
			Amount:       0,
			FrozenAmount: k.Cvn,
		}
		_, err := UpdateCvnAmountBalance(conn, updateAmountData)
		if err != nil {
			Log("err", err.Error())
			conn.Rollback()
			return
		}
		conn.Commit()
	}
}
